package com.edu.whty.common.tools;

import com.edu.whty.anno.ExcelProperty;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;

import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * @author chenzhuo
 * @since 2022/10/11
 */
public class ExcelUtil {

    /**
     * 写excel
     *
     * @param response 响应
     * @param fileName 文件名称
     * @param data     数据
     * @param clazz    clazz
     * @throws IOException 异常
     */
    public static <T> void writeExcel(HttpServletResponse response,
                                      String fileName,
                                      List<T> data,
                                      Class<T> clazz) throws Exception {
        // 第1步，创建一个Workbook，对应一个Excel文件
        HSSFWorkbook wb = new HSSFWorkbook();
        // 第2步，设置表格样式
        HSSFCellStyle headerStyle = setHeaderStyle(wb);
        HSSFCellStyle cellStyle = setCellStyle(wb);
        // 第3步，在Workbook中添加一个sheet，对应Excel文件中的sheet
        HSSFSheet sheet = wb.createSheet(fileName);
        // 第4步，在sheet中添加表头第0行
        HSSFRow row = sheet.createRow(0);
        // 第5步，设置表头
        Field[] fields = clazz.getDeclaredFields();
        int index = 0;
        for (Field field : fields) {
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (Objects.nonNull(property)) {
                HSSFCell cell = row.createCell(index);
                sheet.setColumnWidth(index, property.width() * 256);
                cell.setCellStyle(headerStyle);
                cell.setCellValue(property.value());
                index++;
            }
        }
        // 第6步，设置表格内容
        for (int i = 0; i < data.size(); i++) {
            HSSFRow hssfRow = sheet.createRow(i + 1);
            // 表格列索引
            index = 0;
            for (Field field : fields) {
                // 判断是否是表格字段
                ExcelProperty property = field.getAnnotation(ExcelProperty.class);
                if (Objects.nonNull(property)) {
                    HSSFCell cell = hssfRow.createCell(index++);
                    cell.setCellStyle(cellStyle);
                    // 获取当前字段的getter方法
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                    Method readMethod = descriptor.getReadMethod();
                    // 反射获取字段值
                    Object value = readMethod.invoke(data.get(i));
                    if (Objects.nonNull(value)) {
                        if (value instanceof Number) {
                            cell.setCellValue(((Number) value).doubleValue());
                        } else {
                            cell.setCellValue(value.toString());
                        }
                    }
                }
            }
        }
        // 第7步，返回文件流
        OutputStream os = getOutputStream(fileName, response);
        wb.write(os);
        os.flush();
        os.close();
    }

    /**
     * 写excel
     *
     * @param fileName 文件名称
     * @param data     数据
     * @param clazz    clazz
     * @throws IOException 异常
     */
    public static <T> void writeExcel(String fileName,
                                      List<T> data,
                                      Class<T> clazz,
                                      String filePath) throws Exception {
        // 第1步，创建一个Workbook，对应一个Excel文件
        HSSFWorkbook wb = new HSSFWorkbook();
        // 第2步，设置表格样式
        HSSFCellStyle headerStyle = setHeaderStyle(wb);
        HSSFCellStyle cellStyle = setCellStyle(wb);
        // 第3步，在Workbook中添加一个sheet，对应Excel文件中的sheet
        HSSFSheet sheet = wb.createSheet(fileName);
        // 第4步，在sheet中添加表头第0行
        HSSFRow row = sheet.createRow(0);
        // 第5步，设置表头
        Field[] fields = clazz.getDeclaredFields();
        int index = 0;
        for (Field field : fields) {
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (Objects.nonNull(property)) {
                HSSFCell cell = row.createCell(index);
                sheet.setColumnWidth(index, property.width() * 256);
                cell.setCellStyle(headerStyle);
                cell.setCellValue(property.value());
                index++;
            }
        }
        // 第6步，设置表格内容
        for (int i = 0; i < data.size(); i++) {
            HSSFRow hssfRow = sheet.createRow(i + 1);
            // 表格列索引
            index = 0;
            for (Field field : fields) {
                // 判断是否是表格字段
                ExcelProperty property = field.getAnnotation(ExcelProperty.class);
                if (Objects.nonNull(property)) {
                    HSSFCell cell = hssfRow.createCell(index++);
                    cell.setCellStyle(cellStyle);
                    // 获取当前字段的getter方法
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                    Method readMethod = descriptor.getReadMethod();
                    // 反射获取字段值
                    Object value = readMethod.invoke(data.get(i));
                    if (Objects.nonNull(value)) {
                        if (value instanceof Number) {
                            cell.setCellValue(((Number) value).doubleValue());
                        } else {
                            cell.setCellValue(value.toString());
                        }
                    }
                }
            }
        }
        // 第7步，写入文件
        FileOutputStream fout = new FileOutputStream(filePath);
        wb.write(fout);
        fout.close();
    }

    /**
     * 设置风格
     *
     * @param wb 白平衡
     */
    private static HSSFCellStyle setHeaderStyle(HSSFWorkbook wb) {
        HSSFCellStyle cellStyle = wb.createCellStyle();
        //设置背景色：
        cellStyle.setFillForegroundColor((short) 22);
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 设置边框
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setRightBorderColor((short) 55);
        //设置居中:
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        //设置字体:
        HSSFFont font = wb.createFont();
        font.setColor((short) 63);
        font.setFontName("黑体");
        font.setFontHeightInPoints((short) 12);
        font.setBold(true);
        cellStyle.setFont(font);
        //设置自动换行
        cellStyle.setWrapText(true);

        return cellStyle;
    }

    /**
     * 设置风格
     *
     * @param wb 白平衡
     */
    private static HSSFCellStyle setCellStyle(HSSFWorkbook wb) {
        HSSFCellStyle cellStyle = wb.createCellStyle();
        //设置居中:
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        //设置字体:
        HSSFFont font = wb.createFont();
        font.setColor(Font.COLOR_NORMAL);
        font.setFontName("黑体");
        font.setFontHeightInPoints((short) 10);
        cellStyle.setFont(font);
        //设置自动换行
//        cellStyle.setWrapText(true);

        return cellStyle;
    }

    /**
     * 获得输出流
     *
     * @param fileName 文件名称
     * @param response 响应
     * @return {@link OutputStream}
     * @throws IOException ioexception
     */
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws IOException {
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyExcel没有关系
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20") +
                DateUtils.getDateString(System.currentTimeMillis(), DateUtils.yyyyMMddHHmmss_EN);
        // 这里注意 直接用浏览器或者用postman
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xls");
        response.addHeader("Pargam", "no-cache");
        response.addHeader("Cache-Control", "no-cache");
        return response.getOutputStream();
    }
}

